/*
 *  Copyright (c) 2010, Hugo Augusto Alves
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of the Hugo Augusto Alves nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.liteml.parser.elements;

import java.util.ArrayList;
import java.util.Stack;
import org.liteml.exceptions.LiteParseException;
import org.liteml.parser.LiteUtils;
import org.liteml.parser.LiteUtils.Style;
import org.liteml.parser.LiteUtils.Target;

/**
 *
 * @author Hugo
 */
public class LiteText extends LiteElement {

    private ArrayList<LiteElement> elements = new ArrayList<LiteElement>();

    @Override
    public Type getType() {
        return Type.TEXT;
    }

    @Override
    public String parse(Target t) {
        StringBuilder sb = new StringBuilder();
        switch (t) {
            case HTML:
                for (LiteElement elem : elements) {
                    sb.append(elem.parse(t));
                }
                return sb.toString();
            case PLAIN:
            default:
                for (LiteElement elem : elements) {
                    sb.append(elem.parse(t));
                }
                return sb.toString();
        }
    }

    public LiteText(String str) throws LiteParseException {
        Stack<LiteUtils.Style> markers = new Stack<LiteUtils.Style>();
        boolean comment = false;
        boolean preformatted = false;
        StringBuilder input = new StringBuilder(str);
        StringBuilder newText = new StringBuilder();
        input.append("   "); // for indexes purpose

        char c1 = input.charAt(0);
        char c2 = input.charAt(1);
        char c3 = input.charAt(2);

        LiteElement elem = null;
        for (int i = 0; i < (input.length() - 3); i++, c1 = input.charAt(i), c2 = input.charAt(i + 1), c3 = input.charAt(i + 2)) {
            // preformatted - begin
            if (!preformatted && LiteUtils.isPreformattedBegin(c1, c2, c3)) {
                preformatted = true;
                if (elem != null && newText.length() > 0) {
                    ((LiteStyledText) elem).setText(newText.toString());
                    elements.add(elem);
                } else if (newText.length() > 0) {
                    elements.add(new LiteStyledText(newText.toString()));
                }

                elem = new LiteStyledText();
                newText.delete(0, newText.length());
                ((LiteStyledText) elem).setPreformatted(true);
                i += 2;
                // ignore the extra '\n'
                if (input.charAt(i + 1) == '\n') {
                    i++;
                }
                continue;
            }

            // preformatted - end
            if (preformatted && LiteUtils.isPreformattedEnd(c1, c2, c3)) {
                preformatted = false;
                if (newText.charAt(newText.length() - 1) == '\n') {
                    // ignore the extra '\n'
                    ((LiteStyledText) elem).setText(newText.substring(0, newText.length() - 1));
                } else {
                    ((LiteStyledText) elem).setText(newText.toString());
                }
                elements.add(elem);
                elem = null;
                newText.delete(0, newText.length());
                i += 2;
                continue;
            }

            // preformatted - middle
            if (preformatted) {
                newText.append(c1);
                continue;
            }

            // comment - middle
            if (comment && !LiteUtils.isComment(c1, c2)) {
                continue;
            }
            // comment - begin and end
            if (LiteUtils.isComment(c1, c2)) {
                comment = !comment;
                i++;
                continue;
            }

            // line break
            if (LiteUtils.isBreakLine(c1, c2)) {
                newText.append("\n");
                i++;
                continue;
            }

            // link - begin
            if (LiteUtils.isLinkBegin(c1, c2)) {
                if (elem != null && newText.length() > 0) {
                    ((LiteStyledText) elem).setText(newText.toString());
                    elements.add(elem);
                } else if (newText.length() > 0) {
                    elements.add(new LiteStyledText(newText.toString()));
                }
                newText.delete(0, newText.length());
                i++;
                continue;
            }

            // link - end
            if (LiteUtils.isLinkEnd(c1, c2)) {
                if (newText.length() > 0) {
                    elements.add(new LiteLink(newText.toString()));
                    newText.delete(0, newText.length());
                }

                i++;
                continue;
            }

            // http or ftp link
            if (LiteUtils.isHttpOrFtp(c1, c2, c3)) {
                if (elem != null && newText.length() > 0) {
                    ((LiteStyledText) elem).setText(newText.toString());
                    elements.add(elem);
                } else if (newText.length() > 0) {
                    elements.add(new LiteStyledText(newText.toString()));
                }
                newText.delete(0, newText.length());
                char c = input.charAt(i);
                do {
                    newText.append(c);
                    c = input.charAt(i);
                    i++;
                }while (c != ' ');
                elements.add(new LiteLink(newText.toString()));
                newText.delete(0, newText.length());
                continue;
            }

            // style
            Style s = LiteUtils.isMarker(c1, c2);
            if (s != null) {
                // style marker - begin
                if (!LiteUtils.isOpen(s)) {
                    LiteUtils.open(s);
                    markers.push(s);

                    if (elem != null && newText.length() > 0) {
                        ((LiteStyledText) elem).setText(newText.toString());
                        elements.add(elem);
                    } else if (newText.length() > 0) {
                        elements.add(new LiteStyledText(newText.toString()));
                    }
                    LiteElement tmp = elem;

                    newText.delete(0, newText.length());
                    elem = new LiteStyledText();
                    if (s.equals(Style.BOLD)) {
                        ((LiteStyledText) elem).setBold(true);
                    }
                    if (s.equals(Style.ITALIC)) {
                        ((LiteStyledText) elem).setItalic(true);
                    }
                    if (s.equals(Style.UNDERLINE)) {
                        ((LiteStyledText) elem).setUnderline(true);
                    }
                    if (s.equals(Style.STRIKE)) {
                        ((LiteStyledText) elem).setStrike(true);
                    }
                    if (s.equals(Style.SUPERSCRIPT)) {
                        ((LiteStyledText) elem).setSuperscript(true);
                    }
                    if (s.equals(Style.SUBSCRIPT)) {
                        ((LiteStyledText) elem).setSubscript(true);
                    }

                    if (tmp != null && tmp.getType() == LiteElement.Type.STYLED_TEXT) {
                        ((LiteStyledText) elem).copyStyles((LiteStyledText) tmp);
                    }

                    i++;
                    continue;
                } else {
                    // style marker - end
                    if (!s.equals(markers.pop())) {
                        throw new LiteParseException("The close marker is invalid here: " + str);
                    }

                    LiteUtils.close(s);

                    if (elem != null) {
                        ((LiteStyledText) elem).setText(newText.toString());
                        elements.add(elem);
                    }
                    elem = null;
                    newText.delete(0, newText.length());
                    i++;
                    continue;

                }
            }

            // normal char
            newText.append(c1);

        }
        if (newText.length() > 0) {
            elements.add(new LiteStyledText(newText.toString()));
        }
    }
}
